async function divisionBlock(
  data: Float32Array,
  height: number,
  width: number,
  channel: number,
  blockSize: number,
  padding: number = 0
): Promise<{
  value: Float32Array[]
  destroy: () => void
}> {
  const rootData = data
  /**
   * 矩阵的行数和列数
   */
  const blockRows = Math.ceil(height / blockSize)
  const blockCols = Math.ceil(width / blockSize)
  const padBlockSize = blockSize + padding * 2

  /**
   * 输出的大小
   */
  const outDataLenth =
    blockRows * blockCols * padBlockSize * padBlockSize * channel
  // 初始化WebGPU
  const adapter = await navigator.gpu.requestAdapter()
  if (!adapter) {
    throw new Error('WebGPU not supported')
  }
  const device = await adapter.requestDevice()
  // 创建输入输出缓冲区
  const bufferA = device.createBuffer({
    size: Float32Array.BYTES_PER_ELEMENT * rootData.length,
    usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
  })
  // const debugBuffer = device.createBuffer({
  //   size: Int32Array.BYTES_PER_ELEMENT * 1,
  //   usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
  // })
  const bufferResult = device.createBuffer({
    size: Float32Array.BYTES_PER_ELEMENT * outDataLenth,
    usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
  })

  // 创建用于读取结果的缓冲区
  const readBuffer = device.createBuffer({
    size: Float32Array.BYTES_PER_ELEMENT * outDataLenth,
    usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
  })
  // const readDebugBuffer = device.createBuffer({
  //   size: Int32Array.BYTES_PER_ELEMENT * 1,
  //   usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
  // })

  // 上传数据到GPU
  device.queue.writeBuffer(bufferA, 0, rootData)
  const code = /*wgsl*/ `
  
    struct Matrix {
        values : array<f32>
    }
    struct DebugArray {
        values : array<i32>
    }
    struct Shape {
        h : i32,
        w : i32
    }
    // 分块大小
    const blockSize: i32 = ${blockSize};
    const padBlockSize: i32 = ${padBlockSize};
    const channel : i32 = ${channel};
    const rows: i32 = ${blockRows};
    const cols : i32= ${blockCols};
    const padding : i32 = ${padding};
    const rs = Shape(${height},${width});
    @group(0) @binding(0) var<storage, read> a : Matrix;
    // @group(0) @binding(1) var<storage, read_write> _debug : DebugArray;
    @group(0) @binding(2) var<storage, read_write> result : Matrix;
    
    @compute @workgroup_size(8,8,1)
    fn main(@builtin(global_invocation_id) p : vec3<u32>) {
        // 分块的相对坐标
        let rootIndex = ((i32(p.x) * padBlockSize ) + i32(p.y)) * channel + i32(p.z);
        if i32(p.x) >= padBlockSize || i32(p.y) >= padBlockSize || i32(p.z) >= channel {
          return;
        }
        // _debug.values[0] = i32(2);
        for(var r: i32 = 0; r < rows; r = r + 1) {
            for(var c: i32 = 0; c < cols; c = c + 1) {
                // 原坐标 x
                let aRootX:i32 = ((r * blockSize) - padding) + i32(p.x);
                // 原坐标 y
                let aRootY:i32 = ((c * blockSize) - padding) + i32(p.y);
                // 原矩阵下标
                let aIndex = (aRootX * rs.w + aRootY) * channel + i32(p.z);
                //分块下标+分块内坐标 => 输出下标
                let bIndex = (r * cols + c) * padBlockSize * padBlockSize * channel + rootIndex;
                if aRootX < 0 || aRootY < 0 {
                  result.values[u32(bIndex)] = 0;
                }
                else if aRootX >= rs.h {
                  result.values[u32(bIndex)] = 0;
                }
                // ?玄学??调试了一晚上，不知道为什么下面注释的会导致结果不对
                // else if aRootY > rs.w {
                //     result.values[u32(bIndex)] = 0;
                // }
                else{
                  result.values[u32(bIndex)] = a.values[u32(aIndex)];
                }
            }
        }
    }
    `
  // console.log(code)
  // 创建计算着色器模块
  const shaderModule = device.createShaderModule({
    code,
  })

  // 创建计算管线
  const computePipeline = device.createComputePipeline({
    layout: 'auto',
    compute: {
      module: shaderModule,
      entryPoint: 'main',
    },
  })

  // 创建绑定组
  const bindGroup = device.createBindGroup({
    layout: computePipeline.getBindGroupLayout(0),
    entries: [
      { binding: 0, resource: { buffer: bufferA } },
      // { binding: 1, resource: { buffer: debugBuffer } },
      { binding: 2, resource: { buffer: bufferResult } },
    ],
  })

  // 创建命令编码器
  const commandEncoder = device.createCommandEncoder()

  // 开始计算通道
  const computePass = commandEncoder.beginComputePass()
  computePass.setPipeline(computePipeline)
  computePass.setBindGroup(0, bindGroup)
  console.log('Math.ceil(blockSize / 8)', Math.ceil(blockSize / 8))

  computePass.dispatchWorkgroups(
    Math.ceil(padBlockSize / 8),
    Math.ceil(padBlockSize / 8),
    channel
  )
  computePass.end()

  // 拷贝结果到可读缓冲区
  commandEncoder.copyBufferToBuffer(
    bufferResult,
    0,
    readBuffer,
    0,
    Float32Array.BYTES_PER_ELEMENT * outDataLenth
  )
  // commandEncoder.copyBufferToBuffer(
  //   debugBuffer,
  //   0,
  //   readDebugBuffer,
  //   0,
  //   Int32Array.BYTES_PER_ELEMENT * 1
  // )

  // 提交命令
  device.queue.submit([commandEncoder.finish()])

  // 读取结果
  await readBuffer.mapAsync(GPUMapMode.READ)
  // await readDebugBuffer.mapAsync(GPUMapMode.READ)
  const result = new Float32Array(readBuffer.getMappedRange())
  // const debugReslt = new Int32Array(readDebugBuffer.getMappedRange())
  // console.log('debugReslt:', debugReslt)
  const arr = [] as Float32Array[]
  for (let i = 0; i < blockRows; i++) {
    for (let j = 0; j < blockCols; j++) {
      const blockBegin =
        (i * blockCols + j) * padBlockSize * padBlockSize * channel
      const block = result.subarray(
        blockBegin,
        blockBegin + padBlockSize * padBlockSize * channel
      )
      arr.push(block)
    }
  }
  // device.destroy()
  // readBuffer.destroy()
  bufferA.destroy()
  bufferResult.destroy()
  return {
    value: arr,
    destroy: () => {
      readBuffer.destroy()
      bufferA.destroy()
    },
  }
}
/**
 * 将一个图片分成多个分块，块之间有重合的padding
 * @param data  图片数据 [h,w,c]
 * @param blockSize 分块大小
 */
export default async (
  data: Float32Array,
  height: number,
  width: number,
  channel: number,
  blockSize: number,
  padding: number = 0
) => {
  try {
    const result = await divisionBlock(
      data,
      height,
      width,
      channel,
      blockSize,
      padding
    )
    return result
  } catch (e) {
    console.warn(e)
    return { value: null, destroy: () => {} }
  }
}
